package springboot.study.project.util.proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @ClassName: JdkProxy
 * @Description: 基于JDK的动态代理
 * @Author: liu-hao
 * @Date: 2021-02-20 17:59
 * @Version: 1.0
 **/
public class JdkProxy implements InvocationHandler {

    // 被代理对象
    private Object object;
    // 日志对象
    Logger logger = LoggerFactory.getLogger(JdkProxy.class);

    //这里的目标类型为Object，则可以接受任意一种参数作为被代理类，实现了动态代理。但是要注意下面的newProxyInstance（）中的参数
    public Object getInstance(Object object) {
        this.object = object;
        saveClassFile(object.getClass(), object.getClass().getSimpleName() + "Proxy");//调用保存为class字节码文件
        //与cglib的区别在于这里构建代理对象的时候需要传入被代理对象的接口对象，第二个参数。而cglib不需要被代理对象实现任何接口即可
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        logger.info("---------------jdk前置增强---------------");
        // 注意这里反射调用穿的是目标对象（代理前的对象）
        Object result = method.invoke(object, args);
        logger.info("---------------jdk后置增强---------------");
        return result;
    }

    void saveClassFile(Class clazz, String proxyName) {
        //生成class的字节数组，此处生成的class与proxy.newProxyInstance中生成的class除了代理类的名字不同，其它内容完全一致
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
        String path = clazz.getResource(".").getPath();
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(path + proxyName + ".class");
            fos.write(classFile);//保存到磁盘
            fos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
